#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vector>

using namespace std;

const int defaultfd = -1;   //默认文件fd
const int recontem = 5;     // 重连的次数
const int timeout = 1;      // 重连之间的时间间隔(秒)

enum ConnectState
{
    New = 1,      // 新链接创建
    Connected,    // 连接成功
    DisConnected, // 连接失败
    Close         // 重连后失败,关闭连接
};

class Connection
{
public:
    Connection(const string &ip, uint16_t port)
        : _ip(ip), _port(port), _state(ConnectState::New), _recontem(recontem), _timeout(timeout), _fd(defaultfd)
    {
    }

    ~Connection()
    {
        // RAII管理文件描述符,防止泄漏
        if (_fd != defaultfd)
        {
            close(_fd);
            _fd = defaultfd;
        }
    }

    ConnectState get_state()
    {
        return _state;
    }

    // 连接
    void Connect()
    {
        // 创建tcp socket
        _fd = socket(AF_INET, SOCK_STREAM, 0);
        if (_fd < 0) // 连接失败
        {
            _state = ConnectState::DisConnected;
            return;
        }

        // 发起连接
        sockaddr_in addr;
        memset(&addr, 0, sizeof(sockaddr_in));
        addr.sin_family = AF_INET;
        inet_pton(AF_INET, _ip.c_str(), &addr.sin_addr);
        addr.sin_port = htons(_port);
        int n = connect(_fd, (sockaddr *)&addr, sizeof(addr));
        if (n < 0)
        {          
            DisConnect(); // 关闭连接
            _state = ConnectState::DisConnected;
            return;
        }
        cout << "连接成功" << endl;
        _state = ConnectState::Connected;
    }

    // 重新连接
    void ReConnect()
    {
        int count = 0;
        while (count < _recontem)
        {
            Connect();
            if (_state == ConnectState::Connected)
            {
                return;
            }
            count++;
            sleep(_timeout);
            std::cout << "重连次数: " << count << ", 最大上限: " << recontem << std::endl;
        }
        _state = ConnectState::Close; // 多次重连失败,放弃重连
    }

    void DisConnect() // 关闭连接
    {
        if (_fd != defaultfd)
        {
            close(_fd);
            _fd = defaultfd;
            _state = ConnectState::Close;
        }
    }

    // 连接执行的任务
    void task()
    {
        while (1)
        {
            string send_str = "text code";
            //发送数据
            int n = write(_fd, send_str.c_str(), send_str.size());
            if (n >= 0)
            {
                vector<char> v(1024);
                //读取数据
                int size = read(_fd, v.data(), v.size() - 1);

                if (size > 0)
                {
                    v[size] = 0;
                    cout << v.data() << endl;
                }
                else if(size == 0) //服务端关闭
                {
                    _state = ConnectState::DisConnected;
                    break;
                }
                else //函数执行失败
                {
                    _state = ConnectState::Close;
                    break;
                }
            }
            else //写失败
            {
                _state = ConnectState::Close;
                break;
            }
            sleep(1);
        }
    }

private:
    int _fd;             // 文件描述符
    string _ip;          // 要连接的服务器ip地址
    uint16_t _port;      // 端口号
    ConnectState _state; // 连接状态
    int _recontem;       // 重连的次数
    int _timeout;        // 重连之间的时间间隔
};

class TcpClient
{
public:
    TcpClient(const string &ip, uint16_t port) : _con(ip, port)
    {
    }
    ~TcpClient() {}

    void Loop()
    {
        while (1)
        {
            switch (_con.get_state())
            {
            case ConnectState::New:
                cout << "开始连接" << endl;
                _con.Connect();
                break;
            case ConnectState::Connected:
                cout << "执行任务" << endl;          
                _con.task();
                break;
            case ConnectState::DisConnected:
                cout << "开始重连 " << endl;
                _con.ReConnect();
                break;
            case ConnectState::Close:
                cout << "重连失败,关闭文件" << endl;
                _con.DisConnect();
                return;
            default:
                cout << "错误的状态码" << endl;
                return;
            }
        }
    }

private:
    Connection _con;
};